package main

import (
	"fmt"
	"log"
	"time"

	amqp "github.com/rabbitmq/amqp091-go"
)

// 处理错误并打印日志，如果发生错误则终止程序
func failOnError(err error, msg string) {
	if err != nil {
		log.Panicf("%s: %s", msg, err)
	}
}

func main() {

	// 连接到 RabbitMQ 服务器，并设置心跳间隔为 60 秒，防止连接超时
	conn, err := amqp.DialConfig("amqp://testmq:testmq@127.0.0.1:5672/", amqp.Config{
		Heartbeat: 60 * time.Second,
	})
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close() // 在函数结束时关闭连接

	// 创建一个通道（Channel），通过该通道与 RabbitMQ 进行通信
	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close() // 在函数结束时关闭通道

	// 声明一个队列
	q, err := ch.QueueDeclare(
		"hello", // 队列名称
		true,    // 是否持久化
		false,   // 是否自动删除（当没有消费者连接时是否删除队列）
		false,   // 是否排他（仅限当前连接使用）
		false,   // 等待服务器确认队列创建
		nil,     // 其他参数
	)
	failOnError(err, "Failed to declare a queue")

	// 设置消费端的 QoS（质量保证服务）
	err = ch.Qos(
		30,    // 预取消息数，限制每次最多分配 30 条消息，防止消费者积压过多未处理的消息
		0,     // 预取大小（0 表示不限制单条消息大小）
		false, // 是否应用到整个连接（false 表示只对当前通道生效）
	)
	failOnError(err, "Failed to set QoS")

	// 注册消费者，监听队列中的消息
	msgs, err := ch.Consume(
		q.Name, // 队列名称
		"",     // 消费者名称（留空表示让 RabbitMQ 自动分配）
		false,  // 是否自动确认（auto-ack: false 需要手动确认消息）
		false,  // 是否排他（exclusive: false 允许多个消费者监听同一队列）
		false,  // 是否本地消费（no-local: false 允许连接自己消费自己发布的消息）
		false,  // 是否阻塞（no-wait: false 表示等待服务器返回确认）
		nil,    // 额外参数
	)
	failOnError(err, "Failed to register a consumer")

	// 创建一个阻塞通道，防止主协程退出
	var forever chan struct{}

	// 启动一个 goroutine 监听 RabbitMQ 消息
	go func() {
		for d := range msgs {
			// 业务逻辑处理部分
			fmt.Println(d.Body) // 打印收到的消息

			// 手动确认消息已被正确处理，false 表示仅确认该条消息
			d.Ack(false)
		}
	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	<-forever // 阻塞主线程，使程序保持运行
}
